package org.ovirt.microvirt.services.situation.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.libvirt.Connect;
import org.libvirt.ConnectAuth;
import org.libvirt.ConnectAuthDefault;
import org.libvirt.Domain;
import org.libvirt.LibvirtException;
import org.libvirt.NodeInfo;
import org.libvirt.jna.virConnectCredential;
import org.ovirt.microvirt.beans.Host;
import org.ovirt.microvirt.beans.IpmiLomSettings;
import org.ovirt.microvirt.beans.VirtualMachine;
import org.ovirt.microvirt.beans.WalLomSettings;
import org.ovirt.microvirt.beans.situation.HostState;
import org.ovirt.microvirt.beans.situation.HostStatus;
import org.ovirt.microvirt.services.HostService;
import org.ovirt.microvirt.services.situation.HostSituationService;
import org.ovirt.microvirt.services.situation.lom.LightsOutService;
import org.ovirt.microvirt.services.situation.lom.impl.IpmiLomService;
import org.ovirt.microvirt.services.situation.lom.impl.WalLomService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jna.Pointer;

public class HostSituationServiceImpl implements HostSituationService {

	public HostSituationServiceImpl(HostService hostService) {
		super();
		this.hostService = hostService;
	}

	private final static Logger logger = LoggerFactory
			.getLogger(HostSituationServiceImpl.class);

	private final HostService hostService;

	final static Map<Class<?>, LightsOutService> lomServices = new HashMap<Class<?>, LightsOutService>() {
		private static final long serialVersionUID = 1L;
		{
			put(WalLomSettings.class, new WalLomService());
			put(IpmiLomSettings.class, new IpmiLomService());
		}
	};

	@Override
	public HostStatus getHostStatus(final Host host) {
		Connect libvirt = null;
		try {
			libvirt = connect(host);
			final NodeInfo nodeInfo = libvirt.nodeInfo();
			logger.info(nodeInfo.toString());
			HostStatus status = new HostStatus();
			status.setHostName(host.getName());
			status.setLastChecked(new Date());
			status.setState(HostState.Up);
			return status;
		} catch (LibvirtException e) {
			logger.error(e.getMessage(), e);
			HostStatus status = new HostStatus();
			status.setHostName(host.getName());
			status.setLastChecked(new Date());
			status.setState(HostState.Down);
			return status;
		} finally {
			if (libvirt != null) {
				try {
					libvirt.close();
				} catch (LibvirtException e) {
					logger.warn("Could not close libvirt connection", e);
					// ignore
				}
			}
		}
	}

	private Connect connect(final Host host) throws LibvirtException {
		Connect libvirt;
		ConnectAuthDefault connectAuth = new ConnectAuthDefault();
				new ConnectAuth() {
			@Override
			public int callback(Credential[] arg0) {
				logger.info("authenticating...");
				return 0;
			}

			@Override
			public int authCallback(virConnectCredential arg0, int arg1,
					Pointer arg2) {
				logger.info("authenticating root...");
				arg0.result = host.getRootPassword();
				return 0;
			}
		};
		connectAuth.credType = new ConnectAuth.CredentialType[] { ConnectAuth.CredentialType.VIR_CRED_PASSPHRASE, ConnectAuth.CredentialType.VIR_CRED_USERNAME };
		libvirt = new Connect("qemu+ssh://root@" + host.getAddr() + "/system",
				connectAuth, 0);
		return libvirt;
	}

	@Override
	public HostStatus getHostStatusByName(final String hostName) {
		return getHostStatus(hostService.getById(hostName));
	}

	@Override
	public void powerOff(final String hostName) {
		final Host host = checkHost(hostName);
		final LightsOutService service = lomServices.get(host.getLomSettings()
				.getClass());
		service.powerdown(host);
	}

	private Host checkHost(final String hostName) {
		final Host host = hostService.getById(hostName);
		if (host.getLomSettings() == null) {
			throw new IllegalArgumentException("No powermanagement set");
		}
		return host;
	}

	@Override
	public void powerOn(final String hostName) {
		final Host host = checkHost(hostName);
		final LightsOutService service = lomServices.get(host.getLomSettings()
				.getClass());
		service.powerup(host);
	}

	@Override
	public List<VirtualMachine> listVirtualMachines(String hostName) {
		final Host host = hostService.getById(hostName);
		final ArrayList<VirtualMachine> vms = new ArrayList<VirtualMachine>();
		try {
			final Connect libvirt = connect(host);
			int[] domains = libvirt.listDomains();
			for (int domainId : domains) {
				libvirt.domainLookupByID(domainId);
				vms.add(new VirtualMachine());
			}
		} catch (LibvirtException e) {
			logger.error(e.getMessage());
		}
		return null;
	}

	@Override
	public HostStatus checkHostStatus(final Host host) {
		final HostStatus hostStatus = new HostStatus();
		hostStatus.setLastChecked(new Date());
		Connect libvirt = null;
		try {
			libvirt = connect(host);
			hostStatus.setFreeMemory(libvirt.getFreeMemory());
			hostStatus.setHostName(host.getName());
			hostStatus.setState(HostState.Up);

			String[] listDefinedDomains = libvirt.listDefinedDomains();
			for (String name : listDefinedDomains) {
				Domain domain = libvirt.domainLookupByName(name);
				logger.info(domain.getName() + " " + domain.getInfo().memory);
			}

		} catch (LibvirtException e) {
			return hostStatus;
		} finally {
			if (libvirt != null) {
				try {
					libvirt.close();
				} catch (LibvirtException e1) {
				}
			}
		}
		return hostStatus;
	}
}
